#include "my_hpp/my_config.hpp"
#include <graph/graph.hpp>
#include <graph/shentu_graph.hpp>
#include <vector>

using namespace std;

static constexpr float d = 0.85f;

/**
 * @brief 用以跑自己的的app。需要传xmpi初始化参数，和文件参数
 * @param argc
 * @param argv
 *
 * @return 返回说明
 *
 */
int main(int argc, char **argv) {
    using my_w = float;

    vector<my_idx_score_map_struct> my_wpr;

    xmpi init(&argc, &argv, atoi(argv[1]));
    // 1248-1248-openperf_graph_kubernetes_pr_float

    auto rank = xmpi::get_rank<COMM_WORLD>();

    string fname = argv[2];
    int post = fname.find("-");
    int nvertex_load = stoi(fname.substr(0,post));

    string fpath = my_data_path + fname + ".dat";

    shentu_graph<my_w, 1> graph;
    cout << "read " << nvertex_load << " vertex!" << endl;

    graph.read_from_file(fpath.c_str(), nvertex_load+1);
    // graph.gen_kron(std::atoi(argv[2]), std::atof(argv[3]),
    //                                [](auto &gen) __slave__ { return 1.0f; });

    int DE = xmpi::get_size<COMM_WORLD>() * 2, DH = xmpi::get_size<COMM_COLUMN>() * 4;

    // 进行图预处理
    printf("graph init...\n");
    graph.init(DE, DH, DE, DH, true);

    auto universe = graph.get_vertex_set_universe();
    auto scores = graph.create_vertex_data<float>();
    auto in_degree = graph.create_vertex_data<float>();
    auto out_degree = graph.create_vertex_data<float>();
    auto win_base = graph.create_vertex_data<float>();
    auto wout_base = graph.create_vertex_data<float>();
    auto interm = graph.create_vertex_data<float>();

    // verify
    auto nvertex = graph.get_nvertex() - 1;
    auto lr_cnt = graph.get_lr_cnt();

    PEEK;
    // reverse the graph to calculate win/wout
    // graph.reverse_graph();
    graph.vertex_map(universe, [&](auto v) __slave__ {
        // in_degree[v] = graph.get_in_degree(v);
        // out_degree[v] = graph.get_out_degree(v);
        in_degree[v] = 0;
        out_degree[v] = 0;
        win_base[v] = 0;
        wout_base[v] = 0;
        scores[v] = 1.0 / nvertex;
    });
    PEEK;
    graph.edge_map(
        universe, universe, {}, {&in_degree}, [&](auto u, my_w w) __infer__ { return w; },
        std::plus<float>(),
        [&](auto v, float msg) __infer__ {
            in_degree[v] += msg;
            // printf("rank %d, vertex %d\n", rank, v.value);
        },
        0.f);

    PEEK;

    graph.edge_map(
        universe, universe, {}, {&out_degree}, [&](auto u, my_w w) __infer__ { return w; },
        std::plus<float>(),
        [&](auto v, float msg) __infer__ {
            out_degree[v] += msg;
            // printf("rank %d, vertex %d\n", rank, v.value);
        },
        0.f);

    PEEK;

    graph.edge_map(
        universe, universe, {&out_degree}, {&win_base},
        [&](auto u, my_w w) __infer__ { return out_degree[u]; }, std::plus<float>(),
        [&](auto v, float msg) __infer__ { win_base[v] += msg; }, 0.f);

    PEEK;
    graph.edge_map(
        universe, universe, {&in_degree}, {&wout_base},
        [&](auto u, my_w w) __infer__ { return in_degree[u]; }, std::plus<float>(),
        [&](auto v, float msg) __infer__ { wout_base[v] += msg; }, 0.f);
    PEEK;

    clock_t begin_c, end_c;
    begin_c = clock();
    auto begin_t = MPI_Wtime();
    for (int iter = 0; iter < 8; ++iter) {

        PEEK;
        graph.vertex_map(universe, [&](auto v) __infer__ { interm[v] = 0; });
        PEEK;
        graph.edge_map(
            universe, universe, {&win_base, &wout_base, &scores, &in_degree, &out_degree},
            {&interm},
            [&](auto u, my_w w)
                __infer__ { return 1.0 * scores[u] / (win_base[u] * wout_base[u]); },
            std::plus<float>(),
            [&](auto v, float msg) __infer__ { interm[v] += msg * in_degree[v] * out_degree[v]; },
            0.f);
        PEEK;
        // Update scores and compute delta
        graph.vertex_reduce(
            universe,
            [&](auto v) __slave__ {
                // 对每个顶点，计算新的 score
                float next = 1 - d + d * interm[v];
                // 对比新老 score 并按度数缩放
                // float diff = fabs(next - scores[v]) * std::max(1ul, graph.get_out_degree(v));
                // 更新 scores[v]
                scores[v] = next;
                // 返回 diff 以进行规约，计算 delta
                // return diff;
                return 0;
            },
            // 浮点加的运算和幺元分别是加法和 0f
            std::plus<float>(), 0.f);

        // graph.vertex_map(universe, [&](auto v) __slave__ {
        //     auto origin_v = lr_cnt * rank + v.value;
        //     // my_wpr_float[v.value] = scores[v];
        //     printf("iteration: %d, v: %d, wpr: %f, interm: %f, in_degree: %f, out_degree: %f, "
        //            "w_in_de:%f, w_out_de:%f\n",
        //            iter, origin_v, scores[v], interm[v], in_degree[v], out_degree[v],
        //            win_base[v], wout_base[v]);
        // });
    }
    PEEK;
    auto end_t = MPI_Wtime();
    end_c = clock();

    printf("Iteration Clock Time Cost: %f\n", 1.0 * (end_c - begin_c) / CLOCKS_PER_SEC);

    LOG << "Iteration Time Cost: " << end_t - begin_t << endl;
    LOG << "Iteration Clock Time Cost: " << (end_c - begin_c) / CLOCKS_PER_SEC << endl;
    LOG << "PageRank Performance: " << graph.num_edges() / 1e9 / (end_t - begin_t) * 8 << " GTEPS"
        << std::endl;
    LOG << "Total data storage: " << graph.storage_consumption() / 1e9 << " GB" << std::endl;
    LOG << "Total edges (web links): " << graph.num_edges() / 1e9 << " billion" << std::endl;
    LOG << "Total vertices (web pages): " << graph.num_vertices() / 1e9 << " billion" << std::endl;

    // verify
    // graph.vertex_map(universe, [&](auto v) __slave__ {
    //     auto origin_v = lr_cnt * rank + v.value;//size_t 相对顶点编号
    //     printf("v: %d, wpr: %f\n", origin_v, scores[v]);
    // });

    return 0;
}